package database.queryparser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.TokenStream;

import database.DataFile;
import database.DataManager;
import database.Index;
import database.datastructure.NaiveImpl;
import database.datastructure.ThresholdImpl;
import database.datastructure.WhereImplementation;
import database.pojo.ConditionObj;
import database.pojo.whereObject;

public class SimpleParser {

	private static final Integer INT_CONSTANT_VAL = -1;

	/****************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 ****************************************************************/
	public static void CREATE(StringTokenizer token,String inputStr) throws IllegalArgumentException{
		String temp=token.nextToken();
		if((temp).equalsIgnoreCase("table"))
		{
			CREATE_TABLE(token,inputStr);
		}
		else if((temp).equalsIgnoreCase("index"))
		{
			CREATE_INDEX(token,inputStr);	
		}
		else
		{
			throw new IllegalArgumentException(temp);			
		}
	}

	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	public static void CREATE_TABLE(StringTokenizer token,String inputStr){    	
		String indexON=null, tableName=null;
		ArrayList<String> col = new ArrayList<String>();
		ArrayList<String> colDefn = new ArrayList<String>();
		StringTokenizer st = new StringTokenizer(inputStr,"();,");

		String table=st.nextToken();
		String[] getTable = table.split(" ");
		tableName=getTable[2].toLowerCase();

		while(st.hasMoreTokens())
			colDefn.add(st.nextToken().toLowerCase());

		for(int i=0;i<colDefn.size();i++)
		{
			String[] strDivide=colDefn.get(i).split(" ");
			col.add(strDivide[0]);
			if(colDefn.get(i).contains("Primary Key") || colDefn.get(i).contains("PRIMARY KEY")
					|| colDefn.get(i).contains("primary key"))
				indexON=strDivide[0].toLowerCase();    			
		}

		//Print Values to check
//		for(int j=0;j<col.size();j++)
//			System.out.println("column_name : " + col.get(j));
//		if(indexON!=null)
//			System.out.println("primary_key : " + indexON);

//		System.out.println("Table_name : "+tableName);

		//END Print Values to check

		DataFile df1 = null;

		Map<String, Integer> descriptor1 = new HashMap<String, Integer>();
		ArrayList<String> colunmOrder = new ArrayList<String>();

		for(int j=0;j<col.size();j++)
		{
			colunmOrder.add(col.get(j));
			descriptor1.put(col.get(j), INT_CONSTANT_VAL);
		}

		try {
			df1 = DataManager.createFile(tableName, descriptor1, colunmOrder);
			if(indexON!=null){
				//Index pk1 = df1.createIndex(tableName+"_"+indexON, indexON);
				/*Index pk1 = df1.createIndex(indexON, indexON);*/
				//pk1.dumpIndex();
			}
			df1.dumpFile();
			System.out.println("Table "+tableName+" created successfully." );
			//			//store table info in file...
			//			File newfile = new File("tables.table");
			//			newfile.createNewFile();
			//			FileOutputStream filewrite=new FileOutputStream(newfile);
			//   		    ObjectOutputStream oos = new ObjectOutputStream(filewrite);  		 
			//			
		} catch (Exception e) {
			System.out.println("Table not created. Error occured in table creation");		
		}			

	}	

	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	public static void CREATE_INDEX(StringTokenizer token,String inputStr){
		String tableName,indexONcol,indexName = null;
		try{		
			ArrayList<String> defn = new ArrayList<String>();

			StringTokenizer st = new StringTokenizer(inputStr,"();, \n\t\r\f");
			while(st.hasMoreTokens())
				defn.add(st.nextToken());

			indexName = defn.get(2);
			tableName = defn.get(4);
			indexONcol = defn.get(5);		

			if(DataManager.indexRefs.containsKey(indexName)){
				throw new IllegalArgumentException("Index with name '"+indexName+"' already exists");
			}
			
			//System.out.println(indexName+"\n"+tableName+"\n"+indexONcol);

			DataFile df1 = DataManager.getFile(tableName);
			Index idx1 = df1.createIndex(indexName, indexONcol);
			idx1.dumpIndex();
			df1.dumpFile();
			System.out.println("Index "+indexName+" on column "+indexONcol+" created successfully.");
		} catch(IllegalArgumentException e){
			//e.printStackTrace();
			System.out.println("Error: Index on "+indexName+" not created.");
		}catch(Exception e)	{
			System.out.println("Error: Index on "+indexName+" not created.");
		}
	}

	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	public static void DROP(StringTokenizer token) throws IllegalArgumentException{
		String temp=token.nextToken();
		if(temp.equalsIgnoreCase("table"))
		{
			DROP_TABLE(token);
		}
		else if(temp.equalsIgnoreCase("index"))
		{
			DROP_INDEX(token);	
		}
		else
		{
			throw new IllegalArgumentException(temp);			
		}
	}


	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	private static void DROP_INDEX(StringTokenizer token) {
		String indexParam=token.nextToken();
		try	{
			String index[]=indexParam.split(";");
			String indexName = index[0];
			if(DataManager.indexRefs.containsKey(indexName)){
				String fileName = DataManager.indexRefs.get(indexName);
				DataFile df = DataManager.getFile(fileName);
				df.dropIndex(indexName);
				System.out.println("Index "+indexName+" dropped.");
			}
			else{
				throw new IllegalArgumentException("Index '"+ indexName +"' doesn't exist.");
			}
			System.out.println(indexName);		
		} catch(IllegalArgumentException e){
			System.out.println("Index "+indexParam+" does not exist.");
		}		
	}


	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	private static void DROP_TABLE(StringTokenizer token) {		
		String tableName=token.nextToken();
		String table[];
		try{
			table = tableName.split(";");
			System.out.println(table[0]);
			DataManager.dropFile(table[0]);
			new File(table[0]+".file").delete();
            System.out.println("Table "+tableName+" deleted.");
		} catch(IllegalArgumentException e){
			System.out.println("Table "+tableName+" does not exist.");
		}
	}

	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void SELECT(StringTokenizer token,String inputStr){
		String tableName = null;
		ArrayList<String> selectCols = new ArrayList<String>();
		boolean selectStar = false;
		String orderByExp = null;
		String whereExp = null;
		String orderStr = null;
		int limitValue = 0;
		DataFile df = null;
		ArrayList<Integer> al = new ArrayList<Integer>();
		Map<Integer, Map<String,String>> result = new HashMap<Integer, Map<String,String>>();
		Map<Integer, Integer> topKrecordsThresholdMap = null;
		Map<Integer, Integer> topKrecordsNaiveMap = null;
		try
		{
			StringTokenizer st = new StringTokenizer(inputStr,";,\n\t\r\f ");
			st.nextToken();
			while(st.hasMoreTokens())
			{
				String temp = st.nextToken();
				while(!temp.equalsIgnoreCase("from"))
				{
					if(temp.equalsIgnoreCase("*")){
						selectStar = true;
					}			
					else
					{
						selectCols.add(temp);						
					}
					temp=st.nextToken();
				}
				if(temp.equalsIgnoreCase("from"))
				{
					temp = st.nextToken();
					tableName = temp;
				}
				temp = st.nextToken();
				if(temp.equalsIgnoreCase("where"))
				{
					temp = st.nextToken();
					while(!(temp==null || temp.equalsIgnoreCase(";") || temp.equalsIgnoreCase("order") || temp.equalsIgnoreCase("limit")||
							temp.equalsIgnoreCase("asc") || temp.equalsIgnoreCase("desc") ))
					{
						if(whereExp == null)
							whereExp = temp;
						else
							whereExp = whereExp+temp;

						if(st.hasMoreTokens())
							temp = st.nextToken();
						else
							temp = null;
					}
				}
				if(temp!=null)
				{
					if(temp.equalsIgnoreCase("order"))
					{
						st.nextToken();// this is for "by"
						temp = st.nextToken();
						while( st.hasMoreTokens() && !(temp.equalsIgnoreCase("limit")||
								temp.equalsIgnoreCase("asc") || temp.equalsIgnoreCase("desc") || temp.equalsIgnoreCase(";")))
						{
							if(orderByExp == null)
								orderByExp = temp;
							else
								orderByExp = orderByExp + temp;

							temp = st.nextToken();    			
						}    			
					}
					if(st.hasMoreTokens() && (temp.equalsIgnoreCase("asc") || temp.equalsIgnoreCase("desc")))
					{
						orderStr = temp;
						temp = st.nextToken();
					}
					else
					{
						orderStr = "asc";
					}
					if(st.hasMoreTokens() && temp.equalsIgnoreCase("limit"))
						limitValue = Integer.parseInt(st.nextToken());    	
				}
			}

			if(!(whereExp==null))
			{
				whereObject werob = new whereObject();
				//System.out.println("where "+whereExp);
				df = null;
				df = DataManager.getFile(tableName);
				werob.setRecords(df.getRecordsMap());
				werob.setExpression(whereExp);
				//System.out.println("Before whereImpl()");
				WhereImplementation topk = new WhereImplementation(werob);
				//System.out.println("After whereImpl()");
              
				result = topk.returnResult();
			}
			if(!(orderByExp==null))
			{
				ConditionObj condobj = new ConditionObj();
				condobj.setTableName(tableName);
				condobj.setExpression(orderByExp);

				if(!(orderStr==null))
				{
					if(orderStr.equalsIgnoreCase("desc"))
						condobj.setDescending(true);
					else
						condobj.setDescending(false);				
				}
				else
				{
					condobj.setDescending(true);
				}
				if(limitValue!=0)
				{
					condobj.setTopkValue(limitValue);
				}
				if(!(whereExp==null))            
				{
					condobj.setRecords(result);
					condobj.setWhere(true);
				}
				else
				{					
					df = DataManager.getFile(tableName);
					condobj.setRecords(df.getRecordsMap());
					condobj.setWhere(false);
				}

				long thresholdStartTime = System.currentTimeMillis();
				ThresholdImpl topThresK = new ThresholdImpl(condobj);

				topKrecordsThresholdMap = topThresK.getTopkRecordIdMap();
				long thresholdEndTime = System.currentTimeMillis();
				long thresholdExecutionTime = thresholdEndTime - thresholdStartTime;
				
				long naiveStartTime = System.currentTimeMillis();
				NaiveImpl topNaiveK = new NaiveImpl(condobj);
				topKrecordsNaiveMap = topNaiveK.getTopkRecordIdMap();
				long naiveEndTime = System.currentTimeMillis();
				long naiveExecutionTime = naiveEndTime - naiveStartTime;
				
				System.out.println("Naive Approach Result in "+naiveExecutionTime);
				System.out.println("-----------------------------");
				
				/*for (Integer key : topKrecordsNaiveMap.keySet()) {
					System.out.println("<"+key+", "+topKrecordsNaiveMap.get(key));
				}*/
				
				if(selectStar){
					int k=0;
					Map<String,String> select;
					while(true)
					{
						select = df.getRecordsMap().get(k);
						k++;
						break;
					}

					for(String key:select.keySet())
						selectCols.add(key);
				}
				
				for(int j=0;j<selectCols.size();j++)
				{
					System.out.print(selectCols.get(j)+"\t\t");
				}
				System.out.print("\n");	
				for(Integer key:topKrecordsNaiveMap.keySet())
				{
					for(int j=0;j<selectCols.size();j++)
					{
						System.out.print(df.getRecordsMap().get(key).get(selectCols.get(j))+"\t\t");						
					}
					System.out.print("\n");
				}		
				
				System.out.println("Threshold Approach Result in "+thresholdExecutionTime);
				System.out.println("--------------------------------");
				
				/*for (Integer key : topKrecordsThresholdMap.keySet()) {
					System.out.println("<"+key+", "+topKrecordsThresholdMap.get(key));
				}*/
				
				
				for(int j=0;j<selectCols.size();j++)
				{
					System.out.print(selectCols.get(j)+"\t\t");
				}
				System.out.print("\n");	
				for(Integer key:topKrecordsThresholdMap.keySet())
				{
					for(int j=0;j<selectCols.size();j++)
					{
						System.out.print(df.getRecordsMap().get(key).get(selectCols.get(j))+"\t\t");						
					}
					System.out.print("\n");
				}
			}		
			if((orderByExp==null))
			{
				for(int j=0;j<selectCols.size();j++)
				{
					System.out.print(selectCols.get(j)+"\t\t");
				}
				System.out.print("\n");			


				for(Integer key:result.keySet())
				{
					for(int j=0;j<selectCols.size();j++)
					{
						System.out.print(result.get(key).get(selectCols.get(j))+"\t\t");						
					}
					System.out.print("\n");
				}
			}
		}catch(Exception e){
			//e.printStackTrace();
			System.out.println("Error in SELECT statement.");
		}
		
		
				
	}



	/********************************************************************
	 * 
	 * @param token
	 * @param inputStr
	 *******************************************************************/
	public static DataFile INSERT(StringTokenizer token,String inputStr){
		try
		{
			String tableName=null;
			ArrayList<String> values = new ArrayList<String>();
			//ArrayList<Integer> values = new ArrayList<Integer>();
			ArrayList<String> all = new ArrayList<String>();
			StringTokenizer st= new StringTokenizer(inputStr,"();,\n\t\r\f ");
			while(st.hasMoreTokens())
				all.add(st.nextToken());   	

			tableName = all.get(2);
			for(int i=4;i<all.size();i++)
			{
				values.add(all.get(i));
				//values.add(Integer.parseInt(all.get(i)));
			}

			//System.out.println(tableName);
			DataFile df1 =  null;
			Map<String, String> r = new HashMap<String, String>();

			for(int i=0;i<values.size();i++)
			{
				r.put(Integer.toString(i), values.get(i));
				//System.out.println(values.get(i));
			}		

			df1 = DataManager.getFile(tableName);
			int id = -1;
			id = df1.newInsertRecord(r);
			//df1.dumpFile();
			//df1.viewFile();
			if(id!=-1)
				System.out.println("Successfully inserted row");
			return df1;
		}catch(Exception e){
			System.out.println("Insertion failed");
			return null;
		}

	}


	public static void main(String args[])	
	{
		DataFile df = null;
		int insert_count = 0;
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while(true){
				
				String command = br.readLine();
				command = command.toLowerCase();
				if(command == null || command.equals("")){
					continue;
				}
				//String command="insert into T1 values (1,400,500);";

				/*CharStream CharStream=new ANTLRStringStream(command);
				ParserGrammerLexer lex=new ParserGrammerLexer(CharStream);
				TokenStream input =new CommonTokenStream(lex);
				ParserGrammerParser parse=new ParserGrammerParser(input);
				
				parse.statement();*/

				StringTokenizer st = new StringTokenizer(command);
				String keyword=st.nextToken();
				
				if(! keyword.equalsIgnoreCase("insert") && df != null){
					df.dumpFile();
					df = null;
				}
				else if (insert_count%250 == 0 && df != null){
					df.dumpFile();
					df = null;
				}
				
				if(keyword.equalsIgnoreCase("create"))
				{
					CREATE(st,command);
				}
				else if(keyword.equalsIgnoreCase("drop"))
				{
					DROP(st);
				}
				else if(keyword.equalsIgnoreCase("insert"))
				{
					System.out.print(insert_count++ +": ");
					df = INSERT(st,command);
					//df.dumpFile();
				}
				else if(keyword.equalsIgnoreCase("select"))
				{
					SELECT(st,command);
				}
				else
				{
					System.out.println("Invalid Command!! "+command);
					//throw new IllegalArgumentException("Command");
				}
			}

			//System.out.println("Success");
		} catch (/*Recognition*/Exception e) {			
			//e.printStackTrace();
		}
	}
}
